package com.rookie.shiro.module.util;



import com.rookie.shiro.module.constant.annotation.AnonymousCheck;
import com.rookie.shiro.module.constant.annotation.PermissionDesc;
import com.rookie.shiro.module.entity.PermissionDocEntity;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.MethodUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Controller;
import org.springframework.util.ClassUtils;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 权限工具类
 * 有路径为通配符的bug
 *
 * @author rookie
 */
public class PermissionUtils {

    /**
     * 获取被PermissionDoc注解标注的方法
     *
     * @param context      上下文
     * @param basicPackage 基础包
     * @return 结果
     */
    public static List<PermissionDocEntity> listPermissions(ApplicationContext context, String basicPackage) {
        Map<String, Object> map = context.getBeansWithAnnotation(Controller.class);
        List<PermissionDocEntity> permissions = new ArrayList<>();
        String parentUrl = "";
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            Object bean = entry.getValue();
            if (!StringUtils.contains(ClassUtils.getPackageName(bean.getClass()), basicPackage)) {
                continue;
            }
            Class<?> clz = bean.getClass().getSuperclass();
            if (clz.isAssignableFrom(Object.class)) {
                clz = bean.getClass();
            }
            boolean anonStatus = false;
            if (AnnotationUtils.findAnnotation(clz, AnonymousCheck.class) != null) {
                anonStatus = true;
            }
            RequestMapping clazzRequestMapping = AnnotationUtils.findAnnotation(clz, RequestMapping.class);
            if (clazzRequestMapping != null && clazzRequestMapping.value().length > 0) {
                parentUrl = clazzRequestMapping.value()[0];
                parentUrl = addSlashString(parentUrl);
            }

            initAnon(clz, anonStatus, permissions, parentUrl);

            List<Method> methods = MethodUtils.getMethodsListWithAnnotation(clz, PermissionDesc.class);
            for (Method m : methods) {
                PermissionDocEntity permissionDocEntity = null;
                PermissionDesc p = AnnotationUtils.getAnnotation(m, PermissionDesc.class);
                if (p != null) {

                    permissionDocEntity = new PermissionDocEntity(p.value(), p.message());

                    initUrl(permissionDocEntity, m, parentUrl);
                    permissions.add(permissionDocEntity);
                }

            }
        }
        return permissions;
    }

    /**
     * 初始化匿名方法
     *
     * @param clz        控制层方法
     * @param anon       是否是全局异常
     * @param permission 权限列表
     * @param parentUrl  父级URL
     */
    private static void initAnon(Class<?> clz, boolean anon, List<PermissionDocEntity> permission, String parentUrl) {
        Method[] declaredMethods = clz.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            PermissionDocEntity permissionDocEntity = new PermissionDocEntity();
            if (anon) {
                permissionDocEntity.setAnonymous(true);
                initUrl(permissionDocEntity, declaredMethod, parentUrl);
            } else if (AnnotationUtils.getAnnotation(declaredMethod, AnonymousCheck.class) != null) {
                permissionDocEntity.setAnonymous(true);
                initUrl(permissionDocEntity, declaredMethod, parentUrl);
            } else {
                continue;
            }
            permission.add(permissionDocEntity);
        }

    }

    /**
     * 获取控制器层的所有方法
     */
    public static List<String> listControllerUrl(ApplicationContext context, String basicPackage) {
        Map<String, Object> map = context.getBeansWithAnnotation(Controller.class);
        String parentUrl = "";
        List<String> urlList = new LinkedList<>();
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            Object bean = entry.getValue();
            if (!StringUtils.contains(ClassUtils.getPackageName(bean.getClass()), basicPackage)) {
                continue;
            }
            Class<?> clz = bean.getClass().getSuperclass();
            RequestMapping clazzRequestMapping = AnnotationUtils.findAnnotation(clz, RequestMapping.class);
            if (clazzRequestMapping != null && clazzRequestMapping.value().length > 0) {
                parentUrl = clazzRequestMapping.value()[0];
                parentUrl = addSlashString(parentUrl);
            }

//            获取requestMapping标注的方法
            List<Method> methods = MethodUtils.getMethodsListWithAnnotation(clz, RequestMapping.class);
            for (Method m : methods) {
                RequestMapping requestMapping = AnnotationUtils.getAnnotation(m, RequestMapping.class);
                String str = "";
                if (requestMapping != null && requestMapping.value().length > 0) {
                    str = requestMapping.value()[0];
                    str = addSlashString(str);
                    urlList.add((parentUrl + str).trim());
                }
            }

//            获取post方法
            methods = MethodUtils.getMethodsListWithAnnotation(clz, PostMapping.class);
            for (Method m : methods) {
                PostMapping postMapping = AnnotationUtils.getAnnotation(m, PostMapping.class);
                String str = "";
                if (postMapping != null && postMapping.value().length > 0) {
                    str = postMapping.value()[0];
                    str = addSlashString(str);
                    urlList.add((parentUrl + str).trim());
                }
            }

            //            获取delete方法
            methods = MethodUtils.getMethodsListWithAnnotation(clz, DeleteMapping.class);
            for (Method m : methods) {
                DeleteMapping deleteMapping = AnnotationUtils.getAnnotation(m, DeleteMapping.class);
                String str = "";
                if (deleteMapping != null && deleteMapping.value().length > 0) {
                    str = deleteMapping.value()[0];
                    str = addSlashString(str);
                    urlList.add((parentUrl + str).trim());
                }
            }

            //            获取put方法
            methods = MethodUtils.getMethodsListWithAnnotation(clz, PutMapping.class);
            for (Method m : methods) {
                PutMapping putMapping = AnnotationUtils.getAnnotation(m, PutMapping.class);
                String str = "";
                if (putMapping != null && putMapping.value().length > 0) {
                    str = putMapping.value()[0];
                    str = addSlashString(str);
                    urlList.add((parentUrl + str).trim());
                }
            }

//            获取get方法
            methods = MethodUtils.getMethodsListWithAnnotation(clz, GetMapping.class);
            for (Method m : methods) {
                GetMapping getMapping = AnnotationUtils.getAnnotation(m, GetMapping.class);
                String str = "";
                if (getMapping != null && getMapping.value().length > 0) {
                    str = getMapping.value()[0];
                    str = addSlashString(str);
                    urlList.add((parentUrl + str).trim());
                }
            }
        }
        return urlList;
    }

    /**
     * url 修正
     *
     * @param str url
     * @return 修正后的url
     */
    private static String addSlashString(String str) {
        if (StringUtils.isBlank(str)) {
            return str;
        }
        if (str.charAt(0) != '/') {
            str = "/" + str;
        }
        String appendReg = "/\\{.*}";
        Pattern pattern = Pattern.compile(appendReg);
        Matcher matcher = pattern.matcher(str);
//        如果url 是 /{id} 这种模式则替换成通配符
        if (matcher.find()) {
            String group = matcher.group();
            str = str.replace(group, "/*");
        }
        return str.trim();
    }

    private static void initUrl(PermissionDocEntity permissionDocEntity, Method m, String parentUrl) {
        String str = "";
        RequestMapping requestMapping = AnnotationUtils.getAnnotation(m, RequestMapping.class);
        if (requestMapping != null && requestMapping.value().length > 0) {
            str = requestMapping.value()[0];
            str = addSlashString(str);
            permissionDocEntity.setUrl(parentUrl + str);
        }

        PostMapping postMapping = AnnotationUtils.getAnnotation(m, PostMapping.class);
        if (postMapping != null && postMapping.value().length > 0) {
            str = postMapping.value()[0];
            str = addSlashString(str);
            permissionDocEntity.setUrl(parentUrl + str);
        }

        GetMapping getMapping = AnnotationUtils.getAnnotation(m, GetMapping.class);
        if (getMapping != null && getMapping.value().length > 0) {
            str = getMapping.value()[0];
            str = addSlashString(str);
            permissionDocEntity.setUrl(parentUrl + str);
        }

        DeleteMapping deleteMapping = AnnotationUtils.getAnnotation(m, DeleteMapping.class);
        if (deleteMapping != null && deleteMapping.value().length > 0) {
            str = deleteMapping.value()[0];
            str = addSlashString(str);
            permissionDocEntity.setUrl(parentUrl + str);
        }

        PutMapping putMapping = AnnotationUtils.getAnnotation(m, PutMapping.class);
        if (putMapping != null && putMapping.value().length > 0) {
            str = putMapping.value()[0];
            str = addSlashString(str);
            permissionDocEntity.setUrl(parentUrl + str);
        }
    }
}
